package org.xcorpio.java8.chapter07;

import java.util.stream.LongStream;
import java.util.stream.Stream;

public class ParallelStreams {

    public static void main(String[] args) {
        System.out.println("availableProcessors:" + Runtime.getRuntime().availableProcessors());
        long count = 1000_0000;
        long sequentialStream = measureSumPerf("sequentialStream", ParallelStreams::sequentialSum, count, false);
        System.out.println("seq_i:" + sequentialStream);
        System.out.println("seq_o:" + measureSumPerf("sequentialStream", ParallelStreams::sequentialSum, count, true));
        long parallelSteam = measureSumPerf("parallelSteam", ParallelStreams::parallelSum, count, false);
        System.out.println("pal_i:" + parallelSteam);
        System.out.println("pal_o:" + measureSumPerf("parallelSteam", ParallelStreams::parallelSum, count, true));
        long iteractive = measureSumPerf("iteractive", ParallelStreams::iterativeSum, count, false);
        System.out.println("itr:" + iteractive);
    }

    public static long sequentialSum(long n, boolean optimized) {
        if (optimized) {
            return LongStream.rangeClosed(1, n).reduce(0, Long::sum);
        } else {
            return Stream.iterate(1L, i -> i + 1).limit(n).reduce(0L, Long::sum);
        }
    }

    public static long parallelSum(long n, boolean optimized) {
        if (optimized) {
            return LongStream.rangeClosed(1, n).parallel().reduce(0, Long::sum);
        } else {
            return Stream.iterate(1L, i -> i + 1).limit(n).parallel().reduce(0L, Long::sum);
        }
    }

    public static long iterativeSum(long n, boolean optimized) {
        long sum = 0;
        for (long i = 0; i < n; ++i) {
            sum += i;
        }
        return sum;
    }

    public static long measureSumPerf(String prefix, SumFunction addr, long n, boolean optimized) {
        long fastest = Long.MAX_VALUE;
        for (int i = 0; i < 10; ++i) {
            long start = System.currentTimeMillis();
            addr.apply(n, optimized);
            long duration = System.currentTimeMillis() - start;
            // System.out.println(prefix + ",index:" + i + ",result:" + result +
            // ",takes:" + duration + " ms");
            if (duration < fastest) {
                fastest = duration;
            }
        }
        return fastest;
    }

    interface SumFunction {
        long apply(long a, boolean b);
    }
}
